using System;
using System.Collections.Generic;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;
using NetProtocol.POD;

namespace IQIGame.Onigao.Logic
{
    public class BattlePlayController : TController
    {
        // 回合流程状态机
        public FSMSystem<EBattleState, int> fsm = new FSMSystem<EBattleState, int>();
        // 技能释放状态机 此状态机仅在回合流程TurnState中生效
        public FSMSystem<EBattleSkillState, int> skillFsm = new FSMSystem<EBattleSkillState, int>();

        private Battle _mBattle;
        public Battle battle => this._mBattle;

#region subCtrl

        private BattleTriggerController mTriggerCtrl;
        private BattleUnitController mUnitCtrl;
        private BattleTeamController mTeamCtrl;
        private BattleTurnQueueController mQueueCtrl;
        private BattleUpdateResultContrller mResultCtrl;
        private BattleCollisionController mCollisionCtrl;
        private BattleAIController mAICtrl;
        private BattleMapController mMapCtrl;

        public BattleTriggerController triggerCtrl => this.mTriggerCtrl;
        public BattleUnitController unitCtrl => this.mUnitCtrl;
        public BattleTeamController teamCtrl => this.mTeamCtrl;
        public BattleTurnQueueController queueCtrl => this.mQueueCtrl;
        public BattleUpdateResultContrller resultCtrl => this.mResultCtrl;
        public BattleCollisionController collisionCtrl => this.mCollisionCtrl;
        public BattleAIController AICtrl => this.mAICtrl;
        public BattleMapController mapCtrl => this.mMapCtrl;

#endregion

#region FSM

        // 大循环状态机
        private BattleStartState mStartState;
        private BattleRoundStartState mRoundStartState;
        private BattleRoundEndState mRoundEndState;
        private BattleTurnStartState mTurnStartState;
        private BattleTurnState mTurnState;
        // private BattleActionEndState m_ActionEndState;
        private BattleEndState mEndState;

        // 技能释放状态机
        private BattleSkillNone mSkillNone;
        private BattlePermitCastingState mPermitCasting;
        private BattleSkillExecuteState mSkillExecuteState;

#endregion

        public int battleSceneID;

        public int startRound;
        public int realRound;
        public int strikeTeam;

        public int RoundNum => this.realRound - this.startRound;
        public bool skiping;
        public bool autoBattle;

        // private Dictionary<long, Action> dotDamage = new Dictionary<long, Action>();

        // 判断角色生存
        private Predicate<Unit> unitAlive;

        public void Initialize(Battle battle, CreateBattlePOD enterPOD)
        {
            this._mBattle = battle;
            // SubCtrl
            this.mTriggerCtrl = new BattleTriggerController();
            this.AddSubController(this.triggerCtrl);
            this.mUnitCtrl = new BattleUnitController();
            this.AddSubController(this.mUnitCtrl);
            this.mTeamCtrl = new BattleTeamController();
            this.AddSubController(this.mTeamCtrl);
            this.mQueueCtrl = new BattleTurnQueueController();
            this.AddSubController(this.mQueueCtrl);
            this.mResultCtrl = new BattleUpdateResultContrller();
            this.AddSubController(this.mResultCtrl);
            this.mCollisionCtrl = new BattleCollisionController();
            this.AddSubController(this.mCollisionCtrl);
            this.mAICtrl = new BattleAIController();
            this.AddSubController(this.mAICtrl);
            this.mMapCtrl = new BattleMapController();
            this.AddSubController(this.mMapCtrl);

            //FSM
            this.mStartState = new BattleStartState(this);
            this.fsm.AddState(this.mStartState);
            this.mRoundStartState = new BattleRoundStartState(this);
            this.fsm.AddState(this.mRoundStartState);
            this.mRoundEndState = new BattleRoundEndState(this);
            this.fsm.AddState(this.mRoundEndState);
            this.mTurnStartState = new BattleTurnStartState(this);
            this.fsm.AddState(this.mTurnStartState);
            this.mTurnState = new BattleTurnState(this);
            this.fsm.AddState(this.mTurnState);
            this.mEndState = new BattleEndState(this);
            this.fsm.AddState(this.mEndState);

            // 技能释放的FSM
            this.mPermitCasting = new BattlePermitCastingState(this);
            this.skillFsm.AddState(this.mPermitCasting);
            this.mSkillExecuteState = new BattleSkillExecuteState(this);
            this.skillFsm.AddState(this.mSkillExecuteState);
            this.mSkillNone = new BattleSkillNone(this);
            this.skillFsm.AddState(this.mSkillNone);

            this.battleSceneID = enterPOD.battleScene;
            // 初始化地图
            this.mMapCtrl.Initialize(this, enterPOD.battleScene);
            // 初始化碰撞控制器
            this.mCollisionCtrl.Initialize();
            this.mAICtrl.Initialize(this);
            // 先初始化unit再初始化team
            this.mUnitCtrl.Initialize(this, enterPOD);
            this.mTeamCtrl.Initialize(this, enterPOD);
            this.mQueueCtrl.Initialize(this);

            // 回合数据
            this.startRound = BattleConst.StartRound - 1; //上一阶段最后一回合
            this.strikeTeam = (int)enterPOD.strikeTeam; // 突袭队伍
            this.realRound = this.strikeTeam < 0 ? BattleConst.StartRound : BattleConst.StrikeRound; // 真实当前回合数

            this.mUnitCtrl.OnEnable();
            this.unitAlive = (x) => !x.IsDead();
        }

        public void StartBattle()
        {
            this.fsm.Start(EBattleState.Start);
        }

        public void RoundStart()
        {
            this.realRound++;
            
            this.queueCtrl.RefreshQueue();
            var rQueue = new List<int>();
            this.queueCtrl.GetTurnQueue(ref rQueue);
            var rRoundUpdate = BattleUpdateFactory.CreateRound(this.RoundNum, false, rQueue);
            this.resultCtrl.Add(rRoundUpdate);
            
            this.triggerCtrl.Broadcast(EBattleTriggerType.Battle_RoundStart);
            var rAllUnits = this.unitCtrl.units;
            var nLen = rAllUnits.Count;
            for (int i = 0; i < nLen; i++)
            {
                var nIndex = nLen - i - 1;
                var rUnit = rAllUnits[nIndex];
                if (rUnit.IsDead())
                {
                    continue;
                }
                rUnit.OnRoundStart();
            }
            this.battle.NotifyRound(false);
            
            this.fsm.ChangeState(EBattleState.TurnStart);
        }

        public void RoundEnd()
        {
            var rQueue = new List<int>();
            this.queueCtrl.GetTurnQueue(ref rQueue);
            var rRoundUpdate = BattleUpdateFactory.CreateRound(this.RoundNum, true, rQueue);
            this.resultCtrl.Add(rRoundUpdate);
            this.triggerCtrl.Broadcast(EBattleTriggerType.Battle_RoundEnd);
            var rAllUnits = this.unitCtrl.units;
            var nLen = rAllUnits.Count;
            for (int i = 0; i < nLen; i++)
            {
                var nIndex = nLen - i - 1;
                var rUnit = rAllUnits[nIndex];
                if (rUnit.IsDead())
                {
                    continue;
                }
                rUnit.OnRoundEnd();
            }
            
            this.battle.NotifyRound(true);
        }

        public void ExecuteCurSkill()
        {
            var rCurTurn = this.queueCtrl.current;
            var rSkill = rCurTurn.curSkill;

            var rTargetPos = rCurTurn.Execute();
            this.battle.NotifySkillCast(rSkill != null ? rSkill.executer : rCurTurn.unit, rSkill, rTargetPos);
        }

        public void OnTurnStart()
        {
            this.queueCtrl.current.OnTurnStart(this.realRound);
            var rUnit = this.queueCtrl.current.unit;
            this.triggerCtrl.Broadcast(EBattleTriggerType.Battle_TurnStart, rUnit);
            var rSkillComp = rUnit.GetComponent<SkillComponent>(ETComponentType.SkillComponent);
            rSkillComp.OnTurnStart();
            var rBuffComp = rUnit.GetComponent<BuffComponent>(ETComponentType.BuffComponent);
            rBuffComp.OnDot(EBuffDotTriggerType.TurnStart);

            this.battle.NotifyTurnStart();
        }

        public void TurnEnd()
        {
            if (this.queueCtrl.current == null)
            {
                return;
            }
            this.queueCtrl.current.OnTurnEnd();

            var rBuffComp = this.queueCtrl.current.unit.GetComponent<BuffComponent>(ETComponentType.BuffComponent);
            rBuffComp.OnDot(EBuffDotTriggerType.TurnEnd);
            // 上替补
            foreach (var rPair in this.teamCtrl.teams)
            {
                var rTeam = rPair.Value;
                if (rTeam.subtitute != null)
                {
                    var nDeadUnit = rTeam.units.Find((x) => x.IsDead());
                    if (nDeadUnit != null)
                    {
                        this.Substitute(nDeadUnit.GUID);
                    }
                }
            }

            this.triggerCtrl.Broadcast(EBattleTriggerType.Battle_TurnEnd, this.queueCtrl.current.unit);
            this.fsm.ChangeState(EBattleState.TurnStart);
        }

        public void CastSkill(BattleSkillPOD battleSkillPod, List<LVector2Int> aiPath)
        {
            var rDic = this.unitCtrl.unitsMap;
            if (rDic.TryGetValue(battleSkillPod.unitId, out var rSelf))
            {
                var rWorldPath = ListPool<LVector2>.Get();
                if (aiPath != null && aiPath.Count > 0)
                {
                    var rList = ListPool<int>.Get();
                    var nCount = aiPath.Count;
                    // 去掉首尾
                    for (int i = 1; i < nCount - 1; i++)
                    {
                        var rLastMove = aiPath[i] - aiPath[i - 1];
                        var rNextMove = aiPath[i + 1] - aiPath[i];
                        if (rLastMove == rNextMove)
                        {
                            if (!rList.Contains(i))
                            {
                                rList.Add(i);
                            }
                            continue;
                        }
                        if (i < nCount - 2)
                        {
                            var rNext2Move = aiPath[i + 2] - aiPath[i + 1];
                            if (rNext2Move == rLastMove)
                            {
                                if (!rList.Contains(i))
                                {
                                    rList.Add(i);
                                }
                                if (rList.Contains(i + 1))
                                {
                                    rList.Add(i + 1);
                                }
                            }
                        }
                    }
                    for (int i = rList.Count - 1; i >= 0; i--)
                    {
                        aiPath.RemoveAt(rList[i]);
                    }
                    ListPool<int>.Put(rList);

                    // 在这里剔除连续同向的路径减少传递数据量
                    var rMapCtrl = this.mapCtrl;
                    var rLastSub = LVector2Int.zero;
                    var nStartIndex = 0;
                    if (aiPath.Count > 1)
                    {
                        // 若路径点数量较多，可以跳过第一个点直接去第二个点，避免起点不在地图点上时，初始方向不对的问题
                        nStartIndex = 1;
                    }
                    rWorldPath.Add(rMapCtrl.MapToWorldPos(aiPath[nStartIndex]));
                    for (int j = nStartIndex + 1; j < aiPath.Count; j++)
                    {
                        var rSub = aiPath[j] - aiPath[j - 1];
                        if (rSub != rLastSub || j == aiPath.Count - 1)
                        {
                            rLastSub = rSub;
                            rWorldPath.Add(rMapCtrl.MapToWorldPos(aiPath[j]));
                        }
                    }
                }
                // 有技能朝向用技能朝向，没有用位置朝向
                var rForward = battleSkillPod.targetAspect?.ToLV2() ?? LVector2.zero;
                if (rForward == LVector2.zero)
                {
                    rForward = battleSkillPod.forward?.ToLV2() ?? LVector2.zero;
                }


                rSelf.SetPos(battleSkillPod.pos.ToLV2(), rForward.normalized, rWorldPath);
                ListPool<LVector2>.Put(rWorldPath);
            }

            var rCurTurn = this.queueCtrl.current;
            var rCastUnit = rCurTurn.unit;
            var nCastSkillID = battleSkillPod.skillId;
            var rBuffComp = rCastUnit.GetComponent<BuffComponent>(ETComponentType.BuffComponent);
            if (rBuffComp.replaceSkill.TryGetValue(nCastSkillID, out var nNewID))
            {
                nCastSkillID = nNewID;
            }
            var rSkillComp = rCastUnit.GetComponent<SkillComponent>(ETComponentType.SkillComponent);
            if (rSkillComp.skillDict.TryGetValue(nCastSkillID, out var rSkill))
            {
                if (rSkill == null)
                {
                    LogGame.LogError($"{rCastUnit.GUID}未找到技能:{nCastSkillID}");
                }
                // 切换使用技能
                rCurTurn.curSkill = rSkill;
            }

            rCurTurn.Input(battleSkillPod);
            this.skillFsm.ChangeState(EBattleSkillState.SkillExecute);
        }

        public void SkillComplete()
        {
            if (this.queueCtrl.skillQueue.TryDequeue(out var rSkill))
            {
                this.queueCtrl.current.curSkill = rSkill;
                this.skillFsm.ChangeState(EBattleSkillState.PermitCast);
            }
            else
            {
                this.TurnEnd();
            }
        }

        public bool TryBattleEnd(out int nSuccessTeamIndex)
        {
            nSuccessTeamIndex = -1;
            if (this.realRound > BattleConst.MaxRound)
            {
                nSuccessTeamIndex = BattleConst.TeamBlueIndex;
                return true;
            }

            if (this.teamCtrl.teams.TryGetValue(BattleConst.TeamRedIndex, out var rSelfTeam))
            {
                if (!rSelfTeam.units.Exists(unitAlive))
                {
                    nSuccessTeamIndex = BattleConst.TeamBlueIndex;
                    return true;
                }
            }

            if (this.teamCtrl.teams.TryGetValue(BattleConst.TeamBlueIndex, out var rEnemyTeam))
            {
                if (rEnemyTeam.units.Exists(unitAlive))
                {
                    return false;
                }
                this.RoundEnd();
                if (this.teamCtrl.NextMonsterTeam())
                {
                    this.RoundStart();
                    return false;
                }
                return true;
            }
            return false;
        }

        public void EndBattle(int nSuccessTeam)
        {
            this.fsm.ChangeState(EBattleState.End);
            this.battle.BattleEnd();
        }

        public void SkipBattle()
        {
            this.skiping = true;
            if (this.fsm.currentStateId == EBattleState.Turn && this.skillFsm.currentStateId == EBattleSkillState.PermitCast)
            {
                var rCurTurn = this.queueCtrl.current;
                var rAIComp = rCurTurn.unit.GetComponent<AIComponent>(ETComponentType.AIComponet);
                var rAIMovePath = ListPool<LVector2Int>.Get();
                var rAIInput = rAIComp.GetAIInput(ref rAIMovePath);
                this.CastSkill(rAIInput, rAIMovePath);
                ListPool<LVector2Int>.Put(rAIMovePath);
            }
        }

        public bool AutoBattle(BattleAutoPOD openPOD)
        {
            this.autoBattle = openPOD.isOpen;
            // 根据玩家开自动先不管，有个筛选就行
            var rUnits = this.teamCtrl.teams[BattleConst.TeamRedIndex].units;
            for (int i = 0; i < rUnits.Count; i++)
            {
                rUnits[i].GetComponent<AIComponent>(ETComponentType.AIComponet).active = this.autoBattle;
            }
            // 开启时看当前是不是在等待释放技能，是的话,同步一下当前位置并走AI释放技能
            if (this.autoBattle && this.fsm.currentStateId == EBattleState.Turn && this.skillFsm.currentStateId == EBattleSkillState.PermitCast)
            {
                var rCurTurn = this.queueCtrl.current;
                if (rUnits.Contains(rCurTurn.unit))
                {
                    rCurTurn.unit.SetPos(openPOD.curTempPos.ToLV2(), openPOD.curTempAspect.ToLV2());
                    var rAIComp = rCurTurn.unit.GetComponent<AIComponent>(ETComponentType.AIComponet);
                    var rAIMovePath = ListPool<LVector2Int>.Get();
                    var rAIInput = rAIComp.GetAIInput(ref rAIMovePath);
                    this.CastSkill(rAIInput, rAIMovePath);
                    ListPool<LVector2Int>.Put(rAIMovePath);
                }
            }
            return this.autoBattle;
        }

        public void Substitute(int oldUnit)
        {
            var rOldUnit = this.unitCtrl.GetUnit(oldUnit);
            var rDeadComp = rOldUnit?.GetComponent<DeadComponent>(ETComponentType.DeadComponent);
            if (rDeadComp != null)
            {
                // 旧角色已死，需要打上标记
                rDeadComp.canRelive = false;
                // 召唤新人
                var rOldTeam = this.teamCtrl.teams[rOldUnit.teamIndex];
                if (rOldTeam.subtitute != null)
                {
                    var rPOD = rOldTeam.subtitute;
                    var rSceneConfig = TableCenter.battleScene.Get(battle.playCtrl.battleSceneID);
                    var rPosList = rOldTeam.teamIndex == BattleConst.TeamRedIndex ? rSceneConfig.PlayerBorn[rPOD.formationIndex] : rSceneConfig.MonsterBorn[rPOD.formationIndex];
                    var rBornPos = new LVector2(rPosList[0].ToLFloat(), rPosList[1].ToLFloat());
                    var rBornForward = rOldTeam.teamIndex == BattleConst.TeamRedIndex ? BattleConst.SelfForward : BattleConst.EnemyForward;
                    var rUnit = UnitFactory.CreateBattleUnit(battle, rPOD.configID, rOldTeam.teamIndex, (EUnitType)rPOD.unitType, rPOD.radius.ToLFloat() / LFloat.hundred, rBornPos, rBornForward,
                        rPOD.attrs, rPOD.skills, this.autoBattle || rOldTeam.teamIndex == BattleConst.TeamBlueIndex, rPOD.ai, rPOD.weaknessPoint, (EDamageType)rPOD.weaknessType, rPOD.flankAngle, rPOD.backAngle, rPOD.skinID);
                    this.unitCtrl.AddUnit(rUnit);

                    rOldTeam.subtitute = null;

                    this.battle.NotifySubstitute(rOldUnit, rUnit);
                }
            }
        }

        protected override void OnDispose()
        {
            this.fsm.Dispose();
            this.skillFsm.Dispose();
        }
    }
}